#include "cursor.hpp"
#include <clang-c/Index.h>
#include "parser/cursor.hpp"
#include "utils.hpp"

CursorType::CursorType (const CXType &type):
    handle_ (type) {}
std::string CursorType::display_name () const
{
    return to_string(clang_getTypeSpelling (handle_));
}
int CursorType::argument_count () const
{
    return clang_getNumArgTypes (handle_);
}
CursorType CursorType::argument (unsigned int index) const
{
    return CursorType (clang_getArgType (handle_,index));
}
CursorType CursorType::canonical_type () const
{
    return CursorType (clang_getCanonicalType (handle_));
}
Cursor CursorType::declaration () const
{
    return Cursor (clang_getTypeDeclaration (handle_));
}
CXTypeKind CursorType::kind () const
{
    return handle_.kind;
}
bool CursorType::is_const () const
{
    return clang_isConstQualifiedType (handle_) != 0;
}

Cursor::Cursor (CXCursor cursor):
    handle_ (cursor) {}
CXCursorKind Cursor::kind () const
{
    return clang_getCursorKind (handle_);
}
std::string Cursor::spelling () const
{
    return to_string(clang_getCursorSpelling (handle_));
}
std::string Cursor::display_name () const
{
    return to_string(clang_getCursorDisplayName (handle_));
}
std::string Cursor::source_file () const
{
    auto range = clang_Cursor_getSpellingNameRange(handle_, 0, 0);

    auto start = clang_getRangeStart(range);

    CXFile   file;
    unsigned line, column, offset;

    clang_getFileLocation(start, &file, &line, &column, &offset);

    if (file != nullptr)
        return to_string(clang_getFileName(file));
    return "";
}
bool Cursor::is_definition () const
{
    return clang_isCursorDefinition (handle_);
}
CursorType Cursor::type () const
{
    return CursorType (clang_getCursorType (handle_));
}
Cursor::List Cursor::children (Filter filter) const
{
    struct VisitorData {
        List    children;
        Filter  filter;
    };

    VisitorData vd{{},filter};

    auto visitor = [](CXCursor cursor, CXCursor parent, CXClientData data) {
        auto vd = static_cast<VisitorData*>(data);

        if (!vd->filter || vd->filter(cursor)) {
            vd->children.emplace_back(cursor);
        }

        // if (cursor.kind == CXCursor_ClassDecl) {
        //     Cursor c(cursor);
        //     printf("%s -> %s, in main file? %d\n", c.file_line().c_str(), c.display_name().c_str(), clang_Location_isFromMainFile (clang_getCursorLocation (cursor)));
        // }

        // if (clang_Location_isFromMainFile (clang_getCursorLocation (cursor)))
        // {
        //     container->emplace_back (cursor);
        //     // return CXChildVisit_Recurse;
        // }
        if (cursor.kind == CXCursor_InclusionDirective)
            return CXChildVisit_Break;

        return CXChildVisit_Continue;
    };

    clang_visitChildren(handle_, visitor, &vd);

    return vd.children;
}
void Cursor::visit_children (Cursor::Visitor vis, void *data) {
    clang_visitChildren(handle_, vis, data);
}
std::string Cursor::file_line () const
{
    CXSourceLocation loc = clang_getCursorLocation (handle_);

    CXFile file;
    unsigned int line, col;

    clang_getFileLocation (loc, &file, &line, &col, nullptr);

    std::string info;
    if (!file) {
        info = "<Invalid Source Loc>";
    } else {
        info = to_string(clang_getFileName (file));
        info += ":";
        info += std::to_string(line);
        info += ":";
        info += std::to_string(col);
    }
    return info;
}
